package com.hlkj.baojin.service.impl;

import com.hlkj.baojin.constant.enums.SmsTypeEnum;
import com.hlkj.baojin.dao.IUserDao;
import com.hlkj.baojin.dao.repository.TokenExpirationRepository;
import com.hlkj.baojin.entity.PasswordSet;
import com.hlkj.baojin.entity.User;
import com.hlkj.baojin.jwt.JwtUser;
import com.hlkj.baojin.service.AuthService;
import com.hlkj.baojin.service.IUserService;
import com.hlkj.baojin.service.SmsService;
import com.hlkj.baojin.utils.ErrororUtil;
import com.hlkj.baojin.utils.JwtTokenUtil;
import com.hlkj.baojin.utils.bean.IdGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class AuthServiceImpl implements AuthService {

    private AuthenticationManager authenticationManager;
    private UserDetailsService userDetailsService;
    private JwtTokenUtil jwtTokenUtil;
    private IUserDao userRepository;

    @Autowired
    private SmsService smsService;
    @Autowired
    private IUserService userService;
    @Autowired
    private TokenExpirationRepository tokenExpirationRepository;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    public AuthServiceImpl(
            AuthenticationManager authenticationManager,
            @Qualifier("jwtUserDetailsServiceImpl") UserDetailsService userDetailsService,
            JwtTokenUtil jwtTokenUtil,
            IUserDao userRepository) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.userRepository = userRepository;
    }


    @Override
    public User register(User userToAdd) {

        final String username = userToAdd.getUsername();
        if (userRepository.findByUsername(username) != null) {
            return null;
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        final String rawPassword = userToAdd.getPassword();
        userToAdd.setPassword(encoder.encode(rawPassword));
        userToAdd.setLastPasswordResetDate(System.currentTimeMillis());
        return userRepository.insert(userToAdd);
    }

    @Override
    public String login(String username, String password, String ip) {
        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authentication = null;

        try {
            authentication = authenticationManager.authenticate(upToken);
        } catch (BadCredentialsException exception) {
            ErrororUtil.runThrow(null, "登录", "账号密码错误");
        }
        SecurityContextHolder.getContext().setAuthentication(authentication);
        final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        return jwtTokenUtil.generateToken(userDetails, ip);

    }

    /**
     * 刷新tokern
     */
    @Override
    public String refresh(String oldToken) {
        final String token = oldToken.substring(tokenHead.length());
        String username = jwtTokenUtil.getUsernameFromToken(token);
        JwtUser user = (JwtUser) userDetailsService.loadUserByUsername(username);
        if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())) {
            return jwtTokenUtil.refreshToken(token);
        }
        return null;
    }

    @Override
    public void getCode(String username) {
        User user = userRepository.findByUsernameAndType(username, 1);
        ErrororUtil.isTrueNull(user, "", "不存在当前账户, 请检查账户后重试");
        if (user.getSmsCodeSendTime() != null) {
            ErrororUtil.isTrue(System.currentTimeMillis() - user.getSmsCodeSendTime() < 60 * 1000, "找回密码", "验证吗获取频繁-请一分钟后重试");
        }
        String code = IdGenerator.getInviteCode();
        smsService.sendSms(username, SmsTypeEnum.SECURITY_CODE, code, username);
        user.setSmsCode(code);
        user.setSmsCodeSendTime(System.currentTimeMillis());
        userRepository.save(user);
    }

    @Override
    public Boolean vadicate(String password) {

        JwtUser currentUser = userService.getCurrentUser();
        User user = userService.getUserDao().getById(currentUser.getId());
        return new BCryptPasswordEncoder().matches(password, user.getPassword());
    }

    @Override
    public void forgetPassword(PasswordSet passwordSet) {

        String username = passwordSet.getUsername();
        String code = passwordSet.getCode();
        String password = passwordSet.getPassword();

        User user = userRepository.findByUsernameAndSmsCode(username, code);
        ErrororUtil.isTrueNull(user, "找回密码", "验证码错误");

        ErrororUtil.isTrue(System.currentTimeMillis() - user.getSmsCodeSendTime() > 5 * 60 * 1000, "找回密码", "验证码已过期, 请重新获取");

        user.setPassword(new BCryptPasswordEncoder().encode(password));
        user.setLastPasswordResetDate(System.currentTimeMillis());
        user.setSmsCode(null);
        user.setSmsCodeSendTime(null);
        userRepository.save(user);
    }

    @Override
    public void resetPassword(PasswordSet passwordSet) {
        JwtUser currentUser = userService.getCurrentUser();
        User user = userService.getUserDao().getById(currentUser.getId());

        ErrororUtil.isFalse(new BCryptPasswordEncoder().matches(passwordSet.getOldPassword(), user.getPassword()), "重置密码", "原密码输入错误");
        user.setLastPasswordResetDate(System.currentTimeMillis());
        user.setPassword(new BCryptPasswordEncoder().encode(passwordSet.getPassword()));
        userRepository.save(user);
        tokenExpirationRepository.disableBySubectName(user.getUsername());

    }
}

